/*
 * Copyright (c) 2010-2011, Petr Panteleyev
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *    3. The name of the author may not be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.panteleyev.wordpress;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Box;
import javax.swing.DefaultComboBoxModel;
import javax.swing.ImageIcon;
import javax.swing.JComboBox;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;

import org.panteleyev.blogapi.Blog;
import org.panteleyev.blogapi.WordPressAccount;
import org.panteleyev.blogapi.WordPressComment;
import org.panteleyev.blogapi.WordPressCommentStatus;
import org.panteleyev.blogclient.BloggerFrame;
import org.panteleyev.blogclient.Icon;
import org.panteleyev.blogclient.MainClass;
import org.panteleyev.blogclient.Options;
import org.panteleyev.swing.BaseTable;
import org.panteleyev.swing.OSUtil;
import org.panteleyev.swing.SwingFactory;
import org.panteleyev.swing.TableUtil;
import org.panteleyev.utilities.Updatable;

/**
 *
 * @author Petr Panteleyev <petr@panteleyev.org>
 */
public class WPCommentsModerationFrame extends BloggerFrame<WordPressUserProfile> implements Updatable {
    private WordPressAccount        account;

    private AbstractAction          nextAction;
    private AbstractAction          prevAction;
    private AbstractAction          deleteAction;
    private AbstractAction          editAction;
    private AbstractAction          spamAction;
    private AbstractAction          approveAction;
    private AbstractAction          holdAction;
    private AbstractAction          replyAction;

    private JEditorPane             commentView = new JEditorPane();
    private JTable                  commentTable = new BaseTable();
    private JScrollPane             tableScroller = new JScrollPane(commentTable);
    private AbstractTableModel      tableModel;
    private JComboBox               blogsComboBox;
    private JSplitPane              split;
    private JLabel                  rangeLabel;
    private ArrayList<WordPressComment> comments;

    private CommentHandler          commHandler;

    private int offset;
    private int number = 20;

    private SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy kk:mm");

    private static String[] tableHeaderNames = {
        "id",
        L10N.COMMENTS_MODERATION_AUTHOR.s(),
        L10N.COMMENTS_MODERATION_DATE.s(),
        L10N.COMMENTS_MODERATION_POST_TITLE.s(),
        L10N.COMMENTS_MODERATION_POST_STATUS.s(),
    };

    public WPCommentsModerationFrame(WordPressUserProfile profile) {
        super(profile);
        setTitle(L10N.COMMENTS_MODERATION_FRAME_TITLE.s());
        setIconImage(Icon.COMMENTS.I().getImage());

        account = profile.getAccount();
        commHandler = new CommentHandler(account, this);

        createActions();
        initComponents();
        createMenuBar();
        createPopupMenu();

        update();
    }

    private WordPressAccount getAccount() {
        return account;
    }

    private void createActions() {
        nextAction = new AbstractAction(null, Icon.RIGHT_ARROW.i()) {
            @Override
            public void actionPerformed(ActionEvent e) {
                onNextComments();
            }
        };
        nextAction.putValue(Action.LARGE_ICON_KEY, Icon.RIGHT_ARROW.I());

        prevAction = new AbstractAction(null, Icon.LEFT_ARROW.i()) {
            @Override
            public void actionPerformed(ActionEvent e) {
                onPrevComments();
            }
        };
        prevAction.putValue(Action.LARGE_ICON_KEY, Icon.LEFT_ARROW.I());

        deleteAction = new CommentHandler.CommentDeleteAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                onDeleteComments();
            }
        };

        editAction = new CommentHandler.CommentEditAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                onEditComment();
            }
        };

        replyAction = new CommentHandler.CommentReplyAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                onReply();
            }
        };

        spamAction = new CommentHandler.CommentSpamAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                onSpamComments();
            }
        };

        approveAction = new CommentHandler.CommentApproveAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                onApproveComments();
            }
        };

        holdAction = new CommentHandler.CommentHoldAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                onHoldComments();
            }
        };
    }

    private void initComponents() {
        DefaultComboBoxModel blogListModel = new DefaultComboBoxModel() {
            private ArrayList<Blog> blogs = getAccount().getBlogs();

            @Override
            public int getSize() { return blogs.size(); }
            @Override
            public Object getElementAt(int index) {
                return blogs.get(index).getName();
            }
        };

        tableModel = new AbstractTableModel() {
            @Override
            public int getColumnCount() {
                return tableHeaderNames.length;
            }

            @Override
            public int getRowCount() {
                return (comments == null)? 0 : comments.size();
            }

            @Override
            public Object getValueAt(int row, int col) {
                WordPressComment comm = comments.get(row);

                switch (col) {
                    case 0:
                        return comm.getId();
                    case 1:
                        return comm.getAuthor();
                    case 2:
                        return dateFormat.format(comm.getDateCreated());
                    case 3:
                        return comm.getPostTitle();
                    case 4:
                        switch (comm.getStatus()) {
                            case APPROVE:
                                return Icon.APPROVE.i();
                            case HOLD:
                                return Icon.HOLD.i();
                            case SPAM:
                                return Icon.CANCEL.i();
                        }
                    default:
                        return null;
                }
            }

            @Override
            public Class<?> getColumnClass(int columnIndex) {
                if (columnIndex == 4) {
                    return ImageIcon.class;
                } else {
                    return String.class;
                }
            }

            @Override
            public String getColumnName(int column) {
                return tableHeaderNames[column];
            }
        };
        commentTable.setModel(tableModel);

        commentTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                commentView.setText(commHandler.commentToString(getSelectedComment()));
                enableActions(getUpdateAction().isEnabled());
            }
        });

        tableScroller.setPreferredSize(new Dimension(50, 50));
        tableScroller.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent evt) {
                TableUtil.adjustTableColumnSizes(commentTable, 3, tableScroller);
            }
        });


        getContentPane().setLayout(new BorderLayout());

        getContentPane().add(
            SwingFactory.createToolBarPanel(
                SwingFactory.createToolBar(false, true,
                    replyAction,
                    editAction,
                    deleteAction
                ),
                Box.createHorizontalStrut(5),
                SwingFactory.createToolBar(false, true,
                    spamAction,
                    approveAction,
                    holdAction
                ),
                Box.createHorizontalStrut(5),
                createProfileToolBar(false, true),
                Box.createHorizontalStrut(5),
                blogsComboBox = new JComboBox(blogListModel),
                Box.createHorizontalStrut(5),
                SwingFactory.createToolBar(false, true,
                    prevAction
                ),
                Box.createHorizontalStrut(3),
                rangeLabel = new JLabel(),
                Box.createHorizontalStrut(3),
                SwingFactory.createToolBar(false, true,
                    nextAction
                ),
                Box.createHorizontalGlue(),
                SwingFactory.createToolBar(false, true, getUpdateAction()),
                Box.createHorizontalStrut(5),
                getBusyLabel(),
                Box.createHorizontalStrut(5)
            ), BorderLayout.NORTH
        );

        split = new JSplitPane(JSplitPane.VERTICAL_SPLIT);

        commentView.setContentType("text/html");
        commentView.setEditable(false);
        commentView.addHyperlinkListener(new HyperlinkListener() {
            @Override
            public void hyperlinkUpdate(HyperlinkEvent e) {
                if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
                    OSUtil.runBrowser(e.getURL().toString());
                }
            }
        });

        split.setTopComponent(tableScroller);
        JScrollPane scroll = new JScrollPane(commentView);
        split.setBottomComponent(scroll);

        blogsComboBox.setSelectedIndex(0);
        SwingFactory.setWidthMaximumToPreferred(blogsComboBox);

        getContentPane().add(split, BorderLayout.CENTER);

        Options.getBounds(this);
    }

    private void createMenuBar() {
        createMenuBar(
            createFileMenu(),
            createMenu(L10N.COMMENTS_MENU_NAME.s(), null,
                replyAction,
                editAction,
                null,
                deleteAction,
                null,
                spamAction,
                approveAction,
                holdAction
            ),
            createProfileMenu(),
            createToolsMenu(),
            createWindowMenu(),
            createHelpMenu()
        );
    }

    private void createPopupMenu() {
        tableScroller.setComponentPopupMenu(createMenu(null, null,
            replyAction,
            editAction,
            null,
            deleteAction,
            null,
            spamAction,
            approveAction,
            holdAction
        ).getPopupMenu());
    }

    @Override
    public void update() {
        getBusyLabel().setBusy(true);
        enableActions(false);

        new SwingWorker<ArrayList<WordPressComment>,Object>() {
            @Override
            protected ArrayList<WordPressComment> doInBackground() throws Exception {
                return getAccount().getComments(getSelectedBlog(), null, null, offset, number);
            }

            @Override
            protected void done() {
                try {
                    ArrayList<WordPressComment> newComments = get();
                    comments = newComments;
                    commentView.setText(null);
                } catch (InterruptedException | ExecutionException ex) {
                    handleException(ex);
                } finally {
                    tableModel.fireTableDataChanged();
                    TableUtil.adjustTableColumnSizes(commentTable, 3, tableScroller);
                    split.setDividerLocation(0.5f);

                    StringBuilder text = new StringBuilder();
                    text.append(offset).
                            append(" - ").
                            append(offset + comments.size() - 1);
                    rangeLabel.setText(text.toString());

                    nextAction.setEnabled(comments.size() == number);
                    prevAction.setEnabled(offset > 0);

                    getBusyLabel().setBusy(false);
                    enableActions(true);
                }
            }
        }.execute();
    }

    private WordPressComment getSelectedComment() {
        int[] rows = commentTable.getSelectedRows();
        if (rows.length == 1) {
            return comments.get(rows[0]);
        } else {
            return null;
        }
    }

    private Blog getSelectedBlog() {
        int blogIndex = blogsComboBox.getSelectedIndex();
        return getAccount().getBlog(blogIndex);
    }

    private void onNextComments() {
        offset += number;
        update();
    }

    private void onPrevComments() {
        offset -= number;
        if (offset < 0) {
            offset = 0;
        }
        update();
    }

    private void onDeleteComments() {
        int[] rows = commentTable.getSelectedRows();
        if (rows.length != 0 && MainClass.showConfirmDialog(this)) {
            final ArrayList<WordPressComment> toDelete = new ArrayList<>(rows.length);
            for (int index : rows) {
                toDelete.add(comments.get(index));
            }

            getBusyLabel().setBusy(true);
            enableActions(false);
            offset = 0;
            commHandler.deleteComments(getSelectedBlog(), toDelete);
        }
    }

    private void setCommentsStatus(WordPressCommentStatus status) {
        int[] rows = commentTable.getSelectedRows();
        if (rows.length != 0 && MainClass.showConfirmDialog(this)) {
            final ArrayList<WordPressComment> toEdit = new ArrayList<>(rows.length);
            for (int index : rows) {
                WordPressComment c = comments.get(index);
                c.setStatus(status);
                toEdit.add(c);
            }

            getBusyLabel().setBusy(true);
            enableActions(false);
            offset = 0;
            commHandler.editComments(getSelectedBlog(), toEdit);
        }
    }

    private void onSpamComments() {
        setCommentsStatus(WordPressCommentStatus.SPAM);
    }

    private void onApproveComments() {
        setCommentsStatus(WordPressCommentStatus.APPROVE);
    }

    private void onHoldComments() {
        setCommentsStatus(WordPressCommentStatus.HOLD);
    }

    private void onEditComment() {
        WordPressComment comm = getSelectedComment();

        CommentDialog dial = new CommentDialog(this, comm);
        if (dial.showDialog()) {
            comm.setAuthor(dial.getAuthor());
            comm.setAuthorEmail(dial.getAuthorEmail());
            comm.setAuthorUrl(dial.getAuthorUrl());
            comm.setContent(dial.getContent());
            comm.setStatus(dial.getStatus());

            getBusyLabel().setBusy(true);
            enableActions(false);
            offset = 0;
            commHandler.editComment(getSelectedBlog(), comm);
        }
    }

    private void onReply() {
        WordPressComment replyTo = getSelectedComment();
        if (replyTo != null) {
            WordPressComment comm = new WordPressComment();

            CommentDialog dial = new CommentDialog(this, comm);
            if (dial.showDialog()) {
                comm.setAuthor("");
                comm.setAuthorEmail("");
                comm.setAuthorUrl("");
                comm.setContent(dial.getContent());

                getBusyLabel().setBusy(true);
                enableActions(false);
                offset = 0;
                commHandler.newComment(getSelectedBlog(), replyTo.getPostId().toString(), comm, replyTo.getId());
            }
        }
    }

    private void enableActions(boolean b) {
        if (b) {
            int[] rows = commentTable.getSelectedRows();
            editAction.setEnabled(rows.length == 1);
            replyAction.setEnabled(rows.length == 1);
            deleteAction.setEnabled(rows.length > 0);
            spamAction.setEnabled(rows.length > 0);
            approveAction.setEnabled(rows.length > 0);
            holdAction.setEnabled(rows.length > 0);
        } else {
            editAction.setEnabled(false);
            replyAction.setEnabled(false);
            deleteAction.setEnabled(false);
            spamAction.setEnabled(false);
            approveAction.setEnabled(false);
            holdAction.setEnabled(false);
        }

        getUpdateAction().setEnabled(b);
    }
}
